本系列已集結成書從 0 到 Webpack:學習 Modern Web 專案的建置方式,這是一本完整介紹 Webpack 的專書,如有學習 Webpack 相關的問題可以參考此書。
本文將會學到如何使用 Node.js API 來操作 webpack 。
本文的範例程式放在 peterhpchen/webpack-quest 中,每個程式碼區塊的第一行都會標注檔案的位置,請搭配文章作參考。
Node.js API 是除了 CLI 外另一個操作 webpack 的方法。由於 CLI 會以自己的方式產生輸出資訊與錯誤訊息及 Log ,因此對於有客製建置流程資訊的使用者來說, Node.js API 就是個很好的選擇。
使用 Node.js API 只需要安裝 webpack
核心庫即可:
npm install webpack --save-dev
webpack(configObj, callback)
啟動建置程序安裝後,我們就可以像是 Node.js 函式一樣使用 webpack
模組:
// ./demos/node-interface-callback/build.js
const webpack = require("webpack");
const configurationObject = {
// Configuration Object
};
const callbackFunction = (err, stats) => {
// Callback Function
};
webpack(configurationObject, callbackFunction);
如果 webpack()
有第二個參數 callback function ,那會直接執行編譯,並將結果傳至 callback function 中。
回呼函式會接受兩個參數:
err
: 與 webpack 相關的錯誤,例如錯誤的配置物件stats
: 擁有建置結果的資訊物件利用這兩個函式,可以輸出想要知道的建置過程資訊。
webpack(configObj)
產生 Compiler 實體沒有第二個參數時, webpack()
會傳回編譯器物件(Compiler),可以用它操作 webpack 的建置。
此物件有兩個方法:
run(callback)
: 執行建置watch(watchOptions, callback)
:執行並監聽檔案,發生變化後重新建置,它會傳回 watching
物件,用來操作監聽的動作run(callback)
// ./demos/node-interface-run/build.js
const webpack = require("webpack");
const path = require("path");
const configurationObject = {
// Configuration Object
};
const callbackFunction = (err, stats) => {
// Callback Function
};
const compiler = webpack(configurationObject);
compiler.run(callbackFunction);
run()
會執行建置,當建置完成後會叫用 callback
做輸出資訊的作業,這樣的方式等同於 webpack(configurationObject, callbackFunction)
。
watch(watchOptions, callback)
// ./demos/node-interface-watch/build.js
const webpack = require("webpack");
const path = require("path");
const configurationObject = {
// Configuration Object
};
const callbackFunction = (err, stats) => {
// Callback Function
};
const watchOptions = {
// Watch Options
};
const compiler = webpack(configurationObject);
const watching = compiler.watch(watchOptions, callbackFunction);
使用 watch()
啟動編譯器,會啟動監聽檔案的功能,與 webpack --watch
功能相似。
watchOptions
是監聽相關的設定。
要關閉監聽狀態,可以使用 close(callback)
方法:
watching.close(() => {
console.log("Closed");
});
close()
的 callback
會在結束監聽時叫用。
另外 watching
可以用 invalidate()
取消掉本次的編譯:
watching.invalidate();
下面這裡例子展示了 Compiler 的使用方式:
// ./demos/node-interface-watch/build.js
const webpack = require("webpack");
const path = require("path");
// https://webpack.js.org/configuration/#options
const configurationObject = {
entry: path.resolve(__dirname, "src", "index2.js"),
};
// https://webpack.js.org/api/node/#stats-object
const callbackFunction = (err, stats) => {
if (err) {
// webpack 發生錯誤
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
return;
}
// info 是 Stats Data : https://webpack.js.org/api/stats/#root
const info = stats.toJson();
console.log(`Hash: ${info.hash}`);
console.log(`Version: ${info.version}`);
console.log(`Time: ${info.time}`);
console.log(`Bult at: ${info.builtAt}`);
console.log("\n");
if (stats.hasErrors()) {
// 編譯過程發生錯誤
info.errors.forEach((error) => {
console.error(error);
});
}
if (stats.hasWarnings()) {
// 編譯過程發生警告
info.warnings.forEach((warning) => {
console.warn(warning);
});
}
};
// https://webpack.js.org/configuration/watch/#watchoptions
const watchOptions = {
aggregateTimeout: 2000, // 檔案變動後2秒再重新編譯
};
const compiler = webpack(configurationObject);
const watching = compiler.watch(watchOptions, callbackFunction);
setTimeout(() => {
console.log("\ninvalidate\n");
watching.invalidate();
}, 1000);
setTimeout(() => {
watching.close(() => {
console.log("Closed");
});
}, 5000);
其結果如下圖:
stats.toJson()
會給予詳細的編譯資訊,可以利用這物件組出想要的輸出。invalidate()
後因為前次編譯被視為無效的,因此又重新編譯了一次。
run
與watch
方法都不能夠多併發,必須要等前一次編譯完成才能在執行。
webpack()
與 CLI 的配置檔一樣只要給予陣列的格式,就可以同時編譯多種配置:
// ./demos/node-interface-multiple/build.js
const webpack = require("webpack");
const path = require("path");
const configurationObject = [
{
name: "dev",
mode: "development",
entry: "./src/index2.js",
},
{
name: "prod",
mode: "production",
entry: "./src/index2.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
},
},
];
const callbackFunction = (err, stats) => {
if (err) {
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
return;
}
const info = stats.toJson();
console.log(`Hash: ${info.hash}`);
console.log(`Version: ${info.version}`);
info.children.forEach((child) => {
console.log(`Child ${child.name}`);
console.log(` Hash: ${child.hash}`);
console.log(` Version: ${child.version}`);
console.log(` Time: ${child.time}`);
console.log(` Bult at: ${child.builtAt}`);
console.log("\n");
});
if (stats.hasErrors()) {
// 編譯過程發生錯誤
info.errors.forEach((error) => {
console.error(error);
});
}
if (stats.hasWarnings()) {
// 編譯過程發生警告
info.warnings.forEach((warning) => {
console.warn(warning);
});
}
};
const compiler = webpack(configurationObject);
compiler.run(callbackFunction);
使用陣列選項的 webpack()
傳回的也是傳回 stats
物件,但是在 toJson()
後,每個建置的資訊會在 children
內。
陣列的配置雖然可以同時配置多個不同的設定,但執行時還是一個一個完成,並不會同步執行,如果要同步執行,可以使用 parallel-webpack 來做處理。
Node.js API 在建置完成後會對 callback
傳入 err
及 stats
參數,其中的 stats
是個擁有建置結果資訊的 Stats 物件,在前面的例子我們有使用 stats
取得對應的資訊並做輸出, stats
有下面這些資訊:
webpack CLI 內部就是使用 stats
中的資訊輸出訊息的。
Stats 物件提供了幾個方法:
stats.hasErrors()
: 如果建置有錯誤時為 true
否則為 false
stats.hasWarnings()
: 如果建置有警告時為 true
否則為 false
stats.toJson(statsOptions?)
: 回傳建置資訊, option
可以控制資訊如何輸出
stats.toString(statsOptions?)
: 回傳預設的輸出資訊,與 CLI 輸出的資訊類似在使用 toJson()
後,會取回 Stats 資料 ,這個資料物件會像下面這樣:
{
'version': '5.0.0-alpha.6', // 此編譯使用的 webpack 版本
'hash': '11593e3b3ac85436984a', // 此編譯的 hash 值
'time': 2469, // 此次編譯使用的時間(ms)
'filteredModules': 0, // 此資訊所忽略的模組數量
'outputPath': ''/'', // 此次編譯的輸出路徑
'assetsByChunkName': {
// 此次編譯 chunk 對應的 bundle(asset) 名稱
},
'assets': [
// bundle 的清單
],
'chunks': [
// chunk 的清單
],
'modules': [
// 模組的清單
],
'errors': [
// 錯誤資訊清單
],
'warnings': [
// 警告資訊清單
],
'children' : [
// 當多個建置設定時(配置物件為陣列)的各個建置資訊
]
}
modules
, chunks
及 assets
,分別是輸入、建立中、完成時的資料都可以取得,以便使用者設置輸出資訊。
如果
webpack()
中的配置是陣列時,各個建置資訊會在children
裡。
Node.js API 對於將 webpack 當作建置工具,同時也想要完全客製輸出資訊的使用者來說是較好的選擇。
Node.js API 可以使用配置物件做配置,並且搭配 callback
叫用函式做輸出資訊的控制。
搭配 run
以及 watch
指令控制 webpack ,使用 watch
會監聽檔案的變化做重編譯的動作。
Node.js API 同時也支援陣列的配置物件,可以同時建置多個配置。
Stats 物件提供不同的方法以便使用者取得想要的資料,而 Stats 資料將輸入、建置中、完成時的資料都提供給使用者,以便用來輸出資訊。